/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * uniview
 * Copyright (C) Michael J. Beer 2010 <michael.josef.beer@googlemail.com>
 * 
 * uniview is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * uniview is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "Transformation.h"

namespace uniview {

    Transformation::Transformation() {
        Transformation(Point::DIMENSION, Point::DIMENSION);
        this->m = 0;
        this->n = 0;
    };


    Transformation::Transformation(int m, int n) {
        init(m, n);
    };


    Transformation::Transformation(int m , int n, double **matrix, double *translation) {
        init(m, n);
        for(int i = 0; i < m; i++) {
            memcpy(this->matrix[i], matrix[i], n * sizeof(double));
        };
        memcpy(this->translation, translation, m * sizeof(double));
    };


    Transformation::~Transformation() {
        for(int i = 0; i < m; i++) {
            delete[] this->matrix[i];
        };
        delete[] this->matrix;
        delete[] this->translation;
    };


    Transformation *Transformation::alignToXAxis(double *a, double *origin) {
        if(m != 3 || n != 3)    {
            return 0;
        };  
        memcpy(translation, origin, 3 * sizeof(double));
        Point::multiply(translation, -1.0);

        double x = a[0], y = a[1], z = a[2];
        double ly = x * x;
        double lz = ly + y * y;
        double sa, ca, sb, cb;
        double saM, caM, sbM, cbM;
        if(lz == 0.0) {
            sb = cb = 0.0;
            sbM = cbM = 1.0;
        } else {
            lz = sqrt(lz);
            sb = sbM = x / lz;
            cb = cbM =y / lz;
        };
        ly += z * z; 
        if(ly == 0.0) {
            ca = sa = 0.0;
            caM = saM = 1.0;
        } else {
            ly = sqrt(ly);
            ca = caM = z / ly;
            sa = saM = x / ly;
        };
        matrix[0][0] = caM * cbM; 
        matrix[0][1] = - caM * sbM;
        matrix[0][2] = sa;
        matrix[1][0] = sb;
        matrix[1][1] = cb;
        matrix[1][2] = 0.0;
        matrix[2][0] = cbM * saM;
        matrix[2][1] = - saM * sbM;
        matrix[2][2] = ca;
        return this;
    };


    Transformation *Transformation::rotateX(double angle) {
        if(this->m != 3 || this->n != 0) {
            return 0;
        };
        this->translation[0] = this->translation[1] = this->translation[2] = 0.0;

        double c = cos(angle);
        double s = sin(angle);

        this->matrix[0][0] = 1.0;
        this->matrix[0][1] = 0.0;
        this->matrix[0][2] = 0.0;
        this->matrix[1][0] = 0.0;
        this->matrix[1][1] = 0.0;
        this->matrix[1][2] = 0.0;
        this->matrix[2][0] = 0.0;
        this->matrix[2][1] = 0.0;
        this->matrix[2][2] = 0.0;

        return this;
    };

    Transformation *Transformation::rotateY(double angle) {
        if(this->m != 3 || this->n != 0) {
            return 0;
        };
        this->translation[0] = this->translation[1] = this->translation[2] = 0.0;

        double c = cos(angle);
        double s = sin(angle);

        this->matrix[0][0] = c;
        this->matrix[0][1] = 0.0;
        this->matrix[0][2] = s;
        this->matrix[1][0] = 0.0;
        this->matrix[1][1] = 1.0;
        this->matrix[1][2] = 0.0;
        this->matrix[2][0] = s;
        this->matrix[2][1] = 0.0;
        this->matrix[2][2] = c;

        return this;
    };


    Transformation *Transformation::rotateZ(double angle) {
        if(this->m != 3 || this->n != 0) {
            return 0;
        };
        this->translation[0] = this->translation[1] = this->translation[2] = 0.0;

        double c = cos(angle);
        double s = sin(angle);

        this->matrix[0][0] = c;
        this->matrix[0][1] = -s;
        this->matrix[0][2] = 0.0;
        this->matrix[1][0] = s;
        this->matrix[1][1] = c;
        this->matrix[1][2] = 0.0;
        this->matrix[2][0] = 0.0;
        this->matrix[2][1] = 0.0;
        this->matrix[2][2] = 1.0;

        return this;
    };

    void Transformation::init(int m, int n) {
        this->matrix = Matrix::newMatrix(m, n);
        this->translation = Point::newPoint(m);
        this->m = m;
        this->n = n;
    };
};